package top.ibase4j.core.interceptor;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.alibaba.fastjson.JSON;

import top.ibase4j.core.util.DataUtil;
import top.ibase4j.core.util.FileUtil;
import top.ibase4j.core.util.WebUtil;

/**
 * 非法字符过滤器（防SQL注入，防XSS漏洞）
 *
 * @author ShenHuaJie
 * @version 2017年12月1日 下午2:57:31
 */
public class XssInterceptor extends BaseInterceptor {
    private Logger logger = LogManager.getLogger();

    /**
     * 排除部分URL不做过滤
     */
    private List<String> excludeUrls = new ArrayList<String>();

    /**
     * 公告新增、修改用到富文本，对标签进行转义
     */
    private List<String> noticeUrls = new ArrayList<String>();

    public XssInterceptor() {
        String path = XssInterceptor.class.getResource("/").getFile();
        excludeUrls = FileUtil.readFile(path + "white/xssWhite.txt");
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String uri = request.getRequestURL().toString();
        logger.info("XssInterceptor===>{}", uri);
        Map<String, Object> params = WebUtil.getParameterMap(request);
        logger.info("request parameters===>{}", JSON.toJSONString(params));
        boolean isNoticeUrl = false;
        // 排除部分URL不做过滤。
        if (WebUtil.isWhiteRequest(uri, excludeUrls.size(), excludeUrls)) {
            logger.info("该URL不作校验：" + uri);
            return true;
        }
        for (String st : noticeUrls) {
            if (uri.indexOf(st) >= 0) {
                isNoticeUrl = true;
                break;
            }
        }
        // 获取请求所有参数，校验防止SQL注入，防止XSS漏洞
        String paramN = null;
        for (String key : params.keySet()) {
            if ("sign".equals(key)) {
                logger.info("跳过签名字段");
                continue;
            }
            Object value = params.get(key);
            if (value != null && value instanceof String) {
                String paramVale = value.toString();
                if (key.toLowerCase().contains("password")) {
                    continue;
                }
                if (isNoticeUrl) {
                    paramVale = DataUtil.xssEncode(paramVale);
                }
                // 校验是否存在SQL注入信息
                if (checkSQLInject(paramVale, uri)) {
                    errorResponse(response, paramN);
                    return false;
                }
            }
        }
        return true;
    }

    private void errorResponse(HttpServletResponse response, String paramNm) throws IOException {
        String warning = "输入项中不能包含非法字符。";

        response.setContentType("text/html; charset=UTF-8");
        PrintWriter out = response.getWriter();

        out.println("{\"code\":\"309\",\"msg\":\"" + warning + "\", \"fieldName\": \"" + paramNm + "\"}");
        out.flush();
        out.close();
    }

    /**
     *
     * 检查是否存在非法字符，防止SQL注入
     *
     * @param str
     *            被检查的字符串
     * @return ture-字符串中存在非法字符，false-不存在非法字符
     */
    private boolean checkSQLInject(String str, String url) {
        if (StringUtils.isEmpty(str)) {
            return false;// 如果传入空串则认为不存在非法字符
        }

        if (DataUtil.checkSQLInject(str)) {
            logger.info("xss防攻击拦截url:" + url + "，原因：特殊字符，传入str=" + str);
            return true;
        }
        return false;
    }
}
